#include "pc.hpp"

using namespace std;

struct RtsStatItem final
{
    std::uint64_t t1 = 0;
    std::uint64_t t2 = 0;
};

auto dict = UnorderedMap<std::string, RtsStatItem>{};

int main()
{
    auto fin = std::ifstream("/data/tmp/ngrep.txt", std::ios::binary);
    if (!fin)
    {
        std::cerr << "Invalid fin" << std::endl;
        std::quick_exit(EXIT_FAILURE);
    }

    std::vector<std::size_t> pts;

    auto line = std::string{};

    while (std::getline(fin, line))
    {
        // std::cout << line << std::endl; // ???

        auto mr1 = getMatchResults(
            line,
            rgx<R"(^[^\n]+ ([0-9]{4})/([0-9]{2})/([0-9]{2}) )"
                R"(([0-9]{2}):([0-9]{2}):([0-9]{2})\.([0-9]+)[^\n]+)"
                R"(-> 10\.248\.173\.105:50000 #[^\n]+$)">());

        auto mr2 = getMatchResults(
            line,
            rgx<R"(^[^\n]+ ([0-9]{4})/([0-9]{2})/([0-9]{2}) )"
                R"(([0-9]{2}):([0-9]{2}):([0-9]{2})\.([0-9]+)[^\n]+)"
                R"(10\.248\.173\.105:50000 -> [^\n]+$)">());

        // std::cout << mr1.size() << _I_ << mr2.size() << std::endl;

        if (mr1.empty() && mr2.empty())
        {
            continue;
        }

        if (mr1.size() < 7 && mr2.size() < 7)
        {
            std::cout << "ERROR LINE: " << mr1.size() << _I_ << mr2.size()
                      << _I_ << line << std::endl;
            std::quick_exit(EXIT_FAILURE);
        }

        auto t1 = std::uint64_t{};
        auto t2 = std::uint64_t{};

        if (mr1.size() > 7)
        {
            auto tm    = std::tm{};
            tm.tm_year = (*ton(mr1[1]) - 1900);
            tm.tm_mon  = (*ton(mr1[2]) - 1);
            tm.tm_mday = *ton(mr1[3]);
            tm.tm_hour = *ton(mr1[4]);
            tm.tm_min  = *ton(mr1[5]);
            tm.tm_sec  = *ton(mr1[6]);

            t1 = static_cast<std::uint64_t>(std::mktime(&tm));
            t1 *= 1000000;
            t1 = t1 + *ton(mr1[7]);
        }

        if (mr2.size() > 7)
        {
            auto tm    = std::tm{};
            tm.tm_year = (*ton(mr2[1]) - 1900);
            tm.tm_mon  = (*ton(mr2[2]) - 1);
            tm.tm_mday = *ton(mr2[3]);
            tm.tm_hour = *ton(mr2[4]);
            tm.tm_min  = *ton(mr2[5]);
            tm.tm_sec  = *ton(mr2[6]);

            t2 = static_cast<std::uint64_t>(std::mktime(&tm));
            t2 *= 1000000;
            t2 = t2 + *ton(mr2[7]);
        }

        auto line1 = std::string{};
        auto line2 = std::string{};

        if (!std::getline(fin, line1))
        {
            break;
        }

        if (!std::getline(fin, line2))
        {
            break;
        }

        line1 += line2;

        auto mr3 = getMatchResults(
            line1, rgx<R"(^[^\n]+#(BYTERTS_[0-9]+)\$[^\n]*$)">());
        if (mr3.size() > 1)
        {
            if (t1 > 0)
            {
                dict[tos(mr3[1])].t1 = t1;
            }
            else
            {
                dict[tos(mr3[1])].t2 = t2;
            }

            continue;
        }

        std::cout << "ERROR: " << line1 << std::endl;
        std::quick_exit(EXIT_FAILURE);
    }

    for (auto const& [_, stat] : dict)
    {
        if (stat.t2 > 0 && stat.t1 > 0)
        {
            if (stat.t2 > stat.t1)
            {
                insert(pts, stat.t2 - stat.t1);
            }
        }
    }

    ::ranges::sort(pts);

    for (auto const ts : pts)
    {
        std::cout << ts << std::endl;
    }

    auto const cnt = pts.size();

    auto const idx_pct99 = cnt * 99 / 100;
    auto const idx_pct95 = cnt * 95 / 100;
    auto const idx_pct90 = cnt * 90 / 100;
    auto const idx_pct75 = cnt * 75 / 100;
    auto const idx_pct50 = cnt * 50 / 100;

    std::cout << "Total|PctIndexes: " << cnt << _I_ << idx_pct99 << _I_
              << idx_pct95 << _I_ << idx_pct90 << _I_ << idx_pct75 << _I_
              << idx_pct50 << std::endl;

    std::cout << "pt_pct_99: " << pts[idx_pct99] << std::endl;
    std::cout << "pt_pct_95: " << pts[idx_pct95] << std::endl;
    std::cout << "pt_pct_90: " << pts[idx_pct90] << std::endl;
    std::cout << "pt_pct_75: " << pts[idx_pct75] << std::endl;
    std::cout << "pt_pct_50: " << pts[idx_pct50] << std::endl;
}

/*
auto const svr_ep = LocalEndpoint("127.0.0.1"_ip4, Port(8080));

// Echo server handler
Future<> onNewClientConn(TcpSocket tcp_skt, RemoteEndpoint, LocalEndpoint)
{
    auto       buf    = Buffer(16);
    auto const status = co_await tcp_skt.read(buf, 5s);
    if (status > 0)
    {
        co_await tcp_skt.write(MemView(buf.data(), status), 5s);
    }
}

void benchmark()
{
    sleepFor(1s); // Wait for the Echo server to be ready.

    auto success_cnt           = std::atomic_size_t(0);
    auto failure_cnt           = std::atomic_size_t(0);
    auto rtt_less_than_1ms     = std::atomic_size_t(0);
    auto rtt_less_than_2ms     = std::atomic_size_t(0);
    auto rtt_less_than_3ms     = std::atomic_size_t(0);
    auto rtt_less_than_10ms    = std::atomic_size_t(0);
    auto rtt_greater_than_10ms = std::atomic_size_t(0);

    // Send a string to the Echo server and wait for the response.
    auto const fn_tcp_echo_client = [&] -> Future<>
    {
        auto cli_skt = co_await startTcpClient(svr_ep);

        auto stopwatch = Stopwatch{};
        auto status    = co_await cli_skt.write("hello"sv, 5s);

        if (status > 0)
        {
            auto buf = Buffer(16);
            status   = co_await cli_skt.read(buf, 5s);

            auto const elapsed = stopwatch.elapsed();
            if (elapsed < 1ms)
            {
                ++rtt_less_than_1ms;
            }
            else if (elapsed < 2ms)
            {
                ++rtt_less_than_2ms;
            }
            else if (elapsed < 3ms)
            {
                ++rtt_less_than_3ms;
            }
            else if (elapsed < 10ms)
            {
                ++rtt_less_than_10ms;
            }
            else
            {
                ++rtt_greater_than_10ms;
            }

            if (status > 0 && tosv(buf.data(), status) == "hello"sv)
            {
                ++success_cnt;
                co_return;
            }
        }

        ++failure_cnt;
    };

    auto const total_count = 200'000uz;

    // Start millions of clients concurrently.
    LOOP_N (total_count)
    {
        fn_tcp_echo_client();
    }

    while (success_cnt + failure_cnt < total_count)
    {
        sleepFor(10ms);
    }

    xout("Total Count: "sv, total_count);
    xout("Success Count: "sv, success_cnt.load());
    xout("Failure Count: "sv, failure_cnt.load());
    xout("rtt_less_than_1ms: "sv, rtt_less_than_1ms.load());
    xout("rtt_less_than_2ms: "sv, rtt_less_than_2ms.load());
    xout("rtt_less_than_3ms: "sv, rtt_less_than_3ms.load());
    xout("rtt_less_than_10ms: "sv, rtt_less_than_10ms.load());
    xout("rtt_greater_than_10ms: "sv, rtt_greater_than_10ms.load());
}

int main()
{
    try
    {
        IF_CONSTEVAL
        {
            return 0;
        }
        else
        {
            return 1;
        }

        std::expected<int, std::string> s1 = std::unexpected<std::string>(""s);
        s1                                 = 234;
        s1                                 = std::unexpected(""s);
        auto s                             = std::unexpected(12);
        s                                  = std::unexpected(12);
        auto s3 = std::expected<int, std::string>(std::unexpect, ""s);
        auto s4 = std::expected<int, std::string>(std::unexpect);

        auto s5 = s4;

        initPosixEnv();
        createLogThread("/tmp/stx.log"_fp);
        startTcpServer(svr_ep, onNewClientConn);
        std::thread(benchmark).detach();
        startEventLoop("stx"s);
    }
    catch (std::exception const& e)
    {
        LOG_ERROR("Main thread exited: "sv, e.what());
    }
    catch (...)
    {
        LOG_ERROR("Main thread exited abnormally."sv);
    }

    return EXIT_FAILURE;
}
*/